/**
 * @file ngx_memorypool.h
 * @author like
 * @brief 移植Nginx内存池源代码
 * @version 0.1
 * @date 2025-05-27
 * 
 * @copyright Copyright (c) 2025
 * 
 */

#pragma once

#include <cstddef>
#include <functional>
#include <cstdint>
#include <sys/types.h>  // u_int u_char

constexpr u_int ngx_pagesize = 4096;
constexpr u_int NGX_MAX_ALLOC_FROM_POOL = ngx_pagesize - 1; ///< 小块内存池块允许创建的最大内存
constexpr u_int NGX_ALIGNMENT = sizeof(unsigned long);    /* platform word */
constexpr size_t NGX_DEFAULT_POOL_SIZE = (16 * 1024);   ///< 内存池默认的内存大小

// <stdint.h> 中的 uintptr_t（与指针等宽）
#define ngx_align_ptr(p, a)                                                   \
    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))


using  ngx_pool_cleanup_pt = std::function<void()>;
///@brief 释放外部资源回调函数头部信息
struct ngx_pool_cleanup_t {
    ngx_pool_cleanup_pt   handler;      ///< 外部资源释放回调函数
    ngx_pool_cleanup_t   *next;         ///< 指向下一个外部资源释放回调函数
};

///@brief 大块内存头部信息
struct ngx_pool_large_t {
    ngx_pool_large_t     *next;         ///< 下一个大块内存头信息地址
    void                 *alloc;        ///< 分配的大块内存的起始地址
};

struct ngx_pool_t; // 前置声明

///@brief 小块内存数据头信息
struct ngx_pool_data_t {
    u_char               *last;         ///< 可分配内存起始位置
    u_char               *end;          ///< 可分配内存结束位置
    ngx_pool_t           *next;         ///< 保存下一个小块内存池块的地址
    u_int                failed;        ///< 记录当前小块内存池块分配内存失败的次数
};

///@brief 内存池的主结构体类型
struct ngx_pool_t
{
    ngx_pool_data_t       d;            ///< 小块内存数据头信息
    size_t                max;          ///< 小块内存可分配的最大值
    ngx_pool_t           *current;      ///< 第一个可分配小块内存的内存池起始地址
    ngx_pool_large_t     *large;        ///< 大块内存分配入口指针
    ngx_pool_cleanup_t   *cleanup;      ///< 外部资源清理函数的入口指针
};

class NgxMemoryPool
{
public:
    /**
     * @brief 构造函数(默认申请的内存NGX_DEFAULT_POOL_SIZE)
     * 
     * @param size 开辟小块内存池的大小,单位:字节
     * @note
     * - size如果小(pagesize - 1)就开辟,就开辟size的大小,否则就开辟(pagesize -1)的大小
     * - 实际可用内存为 size - sizeof(ngx_pool_t) (第一块小块内存池)或size - sizeof(ngx_pool_data_t) (第二块小块内存池...最后)
     */
    explicit NgxMemoryPool(size_t size = NGX_DEFAULT_POOL_SIZE);

    /**
     * @brief 析构函数
     *
     * @note 1.释放外部资源,2.释放大块内存,3.释放小块内存
     */
    ~NgxMemoryPool();


    /**
     * @brief 申请内存
     * 
     * @param size 申请内存大小
     * @return void* 申请内存首地址,nullptr内存申请失败
     * @note
     * - 按照NGX_POOL_ALIGNMENT进行内存对齐
     * - 内存不初始化为0
     */
    void *ngx_palloc(size_t size);

    /**
     * @brief 申请内存
     * 
     * @param size 申请内存大小
     * @return void* 申请内存首地址,nullptr内存申请失败
     * @note 不进行内存对齐
     */
    void *ngx_pnalloc(size_t size);

    /**
     * @brief 申请内存
     * 
     * @param size 申请内存大小
     * @return void* 申请内存首地址,nullptr内存申请失败
     * @note
     * - 按照NGX_POOL_ALIGNMENT进行内存对齐
     * - 内存不初始化为0
     */
    void *ngx_pcalloc(size_t size);

    /**
     * @brief 内存重置函数
     * @note 1.释放外部资源,2.释放大块内存,3.重置小块内存
     */
    void ngx_reset_pool();

    /**
     * @brief 释放内存池管理的大块内存
     * 
     * @param p 内存池管理的大块内存首地址
     * @return true 释放内存成功
     * @return false 释放内存失败,该内存不再内存池中管理
     */
    bool ngx_pfree(void *p);

    /**
     * @brief 添加外部资源释放函数
     * 
     * @param callback 外部资源释放回调函数 void() 
     * @return true 添加成功
     * @return false 添加失败
     */
    bool ngx_pool_cleanup_add(ngx_pool_cleanup_pt callback);
private:
    /**
     * @brief 分配小块内存
     * 
     * @param size 申请分配的内存大小
     * @param align 是否进行内存对齐,1:对齐,0:不对齐
     * @return void* 申请内存首地址,nullptr内存申请失败
     */
    void* ngx_palloc_small(size_t size, bool align);

    /**
     * @brief 开辟小块内存池块,并返回用户申请分配的内存首地址
     * 
     * @param size 用户申请分配的内存大小
     * @return void* 分配的内存首地址,nullptr内存分配失败
     */
    void* ngx_palloc_block(size_t size);

    /**
     * @brief 分配大块内存,并返回给用户申请分配的内存首地址
     * 
     * @param size 用户申请分配的内存大小
     * @return void* 分配的内存首地址,nullptr内存分配失败
     */
    void* ngx_palloc_large(size_t size);

private:
    ngx_pool_t* pool_;
};